# -*- coding: utf-8 -*-
"""
  @author: xiaoxiong
  @data:  2021/10/18 21:31
  @function:
"""
import pygame
from .. import tools, setup
from .. import constants as c
from ..components import powerup
import json
import os

class Player(pygame.sprite.Sprite):
    def __init__(self, name):
        pygame.sprite.Sprite.__init__(self)
        self.name = name
        # 载入数据
        self.load_date()
        # 人物的状态
        self.setup_states()
        # 人物移动速录
        self.setup_velocities()
        # 创建一系列时间，比如跑了多久，飞了多久等
        self.setup_timers()
        # 人物的各种帧造型
        self.load_images()

    def load_date(self):
        # 载入玩家JSON文件
        file_name = self.name + '.json'
        file_path = os.path.join('source/data/player', file_name)
        # jsno 转化成了 python 里的字典
        with open(file_path) as f:
            self.player_data = json.load(f)

    def setup_states(self):
        self.face_right = True
        self.dead = False
        self.big = False
        self.fire = False
        # 设置人物状态
        self.state = 'stand'
        # 允许玩家跳跃
        self.can_jump = True
        # 一次一发
        self.can_shoot = True
        # big状态马里奥，碰到怪物时 处于免疫状态
        self.hurt_immune = False

    def setup_velocities(self):
        speed = self.player_data['speed']

        self.x_vel = 0
        self.y_vel = 0

        # vel = velocity 速度 accel = accelorate 加速 turn = turnover 转身 均为正
        # 速度是有方向的
        # 具体改变速度时是应该加上加速度还是减去加速度根据玩家的状态来决定
        self.max_walk_vel = speed["max_walk_speed"]
        self.max_run_vel = speed["max_run_speed"]
        self.max_y_vel = speed["max_y_velocity"]
        self.jump_vel = speed["jump_velocity"]
        self.walk_accel = speed["walk_accel"]
        self.run_accel = speed["run_accel"]
        # 急刹车转身时的加速度
        self.turn_accel = speed["turn_accel"]
        # 重力加速度也属于敌人，整个游戏世界，所以放在 constants.py 中
        self.gravity = c.GRAVITY
        self.anti_gravity = c.ANTI_GRAVITY

        # 初始数据
        self.max_x_vel = self.max_walk_vel
        self.x_accel = self.walk_accel

    def setup_timers(self):
        self.walking_timer = 0
        self.transition_timer = 0
        self.death_timer = 0
        self.hurt_immune_timer = 0
        self.last_fireball_timer = 0

    def load_images(self):
        sheet = setup.GRAPHICS['mario_bros']
        '''
        # 测试人物移动代码
        self.right_frames = []
        self.left_frames = []
        self.up_frames = []
        self.down_frames = []

        # self.frames = []
        frame_rects = {
            (178, 32, 12, 16),
            (80, 32, 15, 16),
            (96, 32, 16, 16),
            (112, 32, 16, 16)
        }
        # self.frames.append(tools.get_image(sheet, 178, 32, 12, 16, (0, 0, 0), c.BG_MULTI))

        for frame_rect in frame_rects:
            right_image = tools.get_image(sheet, *frame_rect, (0, 0, 0), c.BG_MULTI)
            # 镜面翻转 图片，左右是否翻转，上下是否翻转
            left_image = pygame.transform.flip(right_image, True, False)
            up_image = pygame.transform.rotate(right_image, 90)
            down_image = pygame.transform.rotate(right_image, -90)
            # 添加到列表里
            self.right_frames.append(right_image)
            self.left_frames.append(left_image)
            self.up_frames.append(up_image)
            self.down_frames.append(down_image)
        
        '''

        frame_rects = self.player_data['image_frames']

        self.right_small_normal_frames = []
        self.right_big_normal_frames = []
        self.right_big_fire_frames = []
        self.left_small_normal_frames = []
        self.left_big_normal_frames = []
        self.left_big_fire_frames = []

        self.small_normal_frames = [self.right_small_normal_frames, self.left_small_normal_frames]
        self.big_normal_frames = [self.right_big_normal_frames, self.left_big_normal_frames]
        self.big_fire_frames = [self.right_big_fire_frames, self.left_big_fire_frames]

        self.all_frames = [
            self.right_small_normal_frames,
            self.right_big_normal_frames,
            self.right_big_fire_frames,
            self.left_small_normal_frames,
            self.left_big_normal_frames,
            self.left_big_fire_frames
        ]

        self.right_frames = self.right_small_normal_frames
        self.left_frames = self.left_small_normal_frames

        for group, group_frame_rects in frame_rects.items():
            for frame_rect in group_frame_rects:
                right_image = tools.get_image(sheet, frame_rect['x'], frame_rect['y'],
                                                   frame_rect['width'], frame_rect['height'], (0, 0, 0), c.BG_MULTI)
                left_image = pygame.transform.flip(right_image, True, False)
                if group == 'right_small_normal':
                    self.right_small_normal_frames.append(right_image)
                    self.left_small_normal_frames.append(left_image)
                if group == 'right_big_normal':
                    self.right_big_normal_frames.append(right_image)
                    self.left_big_normal_frames.append(left_image)
                if group == 'right_big_fire':
                    self.right_big_fire_frames.append(right_image)
                    self.left_big_fire_frames.append(left_image)

        self.frame_index = 0
        self.frames = self.right_frames
        self.image = self.frames[self.frame_index]
        self.rect = self.image.get_rect()

    def update(self, keys, level):
        #计时器
        self.current_time = pygame.time.get_ticks()
        self.handle_states(keys, level)
        self.is_hurt_immune()
        # # 这里只改变人物速度，根据速度计算人物位置的事交给游戏关卡来做
        # if keys[pygame.K_RIGHT]:
        #     self.state = 'walk'
        #     self.x_vel = 5
        #     self.y_vel = 0
        #     self.frames = self.right_frames
        # if keys[pygame.K_LEFT]:
        #     self.state = 'walk'
        #     self.x_vel = -5
        #     self.y_vel = 0
        #     self.frames = self.left_frames
        # '''
        # if keys[pygame.K_UP]:
        #     self.x_vel = 0
        #     self.y_vel = -5
        #     self.frames = self.up_frames
        # if keys[pygame.K_DOWN]:
        #     self.x_vel = 0
        #     self.y_vel = 5
        #     self.frames = self.down_frames
        # '''
        # if keys[pygame.K_SPACE]:
        #     self.state = 'jump'
        #     self.y_vel = -5
        # if self.state == 'walk':
        #     if self.current_time - self.walking_timer >1000:
        #         self.walking_timer = self.current_time
        #         self.frame_index += 1
        #         self.frame_index %= 4
        # if self.state == 'jump':
        #     self.frame_index = 4
        # self.image = self.frames[self.frame_index]

    def handle_states(self, keys, level):
        # 判断是否能跳跃
        self.can_jump_or_not(keys)
        # 是否能发射
        self.can_shoot_or_not(keys)

        if self.state == 'stand':
            self.stand(keys, level)
        elif self.state == 'walk':
            self.walk(keys, level)
        elif self.state == 'jump':
            self.jump(keys, level)
        elif self.state == 'fall':
            self.fall(keys, level)
        elif self.state == 'die':
            self.die(keys)
        elif self.state == 'small2big':
            self.small2big(keys)
        elif self.state == 'big2small':
            self.big2small(keys)
        elif self.state == 'big2fire':
            self.big2fire(keys)

        if self.face_right:
            self.image = self.right_frames[self.frame_index]
        else:
            self.image = self.left_frames[self.frame_index]

    def can_jump_or_not(self, keys):
        if not keys[pygame.K_a]:
            self.can_jump = True

    def can_shoot_or_not(self, keys):
        if not keys[pygame.K_s]:
            self.can_shoot = True

    def stand(self, keys, level):
        self.frame_index = 0
        self.x_vel = 0
        self.y_vel = 0
        if keys[pygame.K_RIGHT]:
            self.face_right = True
            self.state = 'walk'
        elif keys[pygame.K_LEFT]:
            self.face_right = False
            self.state = 'walk'
        elif keys[pygame.K_a] and self.can_jump:
            self.state = 'jump'
            self.y_vel = self.jump_vel
        elif keys[pygame.K_s]:
            if self.fire and self.can_shoot:
                self.shoot_fireball(level)

    def walk(self, keys, level):
        # sprint 冲刺， shoot 发射
        if keys[pygame.K_s]:
            self.max_x_vel = self.max_run_vel
            self.x_accel = self.run_accel
            if self.fire and self.can_shoot:
                self.shoot_fireball(level)
        else:
            self.max_x_vel = self.max_walk_vel
            self.x_accel = self.walk_accel

        # 支持跳跃
        if keys[pygame.K_a] and self.can_jump:
            self.state = 'jump'
            self.y_vel = self.jump_vel

        # 帧切换
        if self.current_time - self.walking_timer > self.calc_frame_duration():
            if self.frame_index < 3:
                self.frame_index += 1
            else:
                self.frame_index = 1
            self.walking_timer = self.current_time

        # 行走时按下不同按键时的状态
        '''
         这里分两种情况，人物往左走(此时 x_vel < 0)时按下右键，为刹车效果
                         人物往右走(此时 x_vel > 0)时按下右键，为加速效果
                         不同状态使用的加速度方向和帧不同
        '''
        if keys[pygame.K_RIGHT]:
            self.face_right = True
            if self.x_vel < 0:
                self.frame_index = 5
                self.x_accel = self.turn_accel
            self.x_vel = self.calc_vel(self.x_vel, self.x_accel, self.max_x_vel, True)
        elif keys[pygame.K_LEFT]:
            self.face_right = False
            if self.x_vel > 0:
                # 临时代码，代表刹车帧
                self.frame_index = 5
                self.x_accel = self.turn_accel
            self.x_vel = self.calc_vel(self.x_vel, self.x_accel, self.max_x_vel, False)
        # 这里表示没有按下任何键时是站立状态
        else:
            if self.face_right:
                self.x_vel -= self.x_accel
                # 从一个正的速度减为负的，说明人物已经停下来了
                if self.x_vel < 0:
                    self.x_vel = 0
                    self.state = 'stand'
            else:
                self.x_vel += self.x_accel
                if self.x_vel > 0:
                    self.x_vel = 0
                    self.state = 'stand'

    def jump(self, keys, level):
        self.frame_index = 4
        self.y_vel += self.anti_gravity
        # 一旦起跳，不能再跳
        self.can_jump = False

        if self.y_vel >= 0:
            self.state = 'fall'

        if keys[pygame.K_RIGHT]:
            self.x_vel = self.calc_vel(self.x_vel, self.x_accel, self.max_x_vel, True)
        elif keys[pygame.K_LEFT]:
            self.x_vel = self.calc_vel(self.x_vel, self.x_accel, self.max_x_vel, False)
        elif keys[pygame.K_s]:
            if self.fire and self.can_shoot:
                self.shoot_fireball(level)

        if not keys[pygame.K_a]:
            self.state = 'fall'

    def fall(self, keys, level):
        self.y_vel = self.calc_vel(self.y_vel, self.gravity, self.max_y_vel)

        # # TODO 临时代码，之后会迁移到 level,py
        # if self.rect.bottom > c.GROUND_HEIGHT:
        #     self.rect.bottom = c.GROUND_HEIGHT
        #     self.y_vel = 0
        #     self.state = 'walk'

        if keys[pygame.K_RIGHT]:
            self.x_vel = self.calc_vel(self.x_vel, self.x_accel, self.max_x_vel, True)
        elif keys[pygame.K_LEFT]:
            self.x_vel = self.calc_vel(self.x_vel, self.x_accel, self.max_x_vel, False)
        elif keys[pygame.K_s]:
            if self.fire and self.can_shoot:
                self.shoot_fireball(level)

    def die(self, keys):
        self.rect.y += self.y_vel
        self.y_vel += self.anti_gravity

    def small2big(self, keys):
        frame_dur = 65
        sizes = [1, 0, 1, 0, 1, 2, 0, 1, 2, 0, 2]
        frames_and_idx = [(self.small_normal_frames, 0), (self.small_normal_frames, 7), (self.big_normal_frames, 0)]

        if self.transition_timer == 0:
            self.big = True
            self.transition_timer = self.current_time
            self.changing_idx = 0
        elif self.current_time - self.transition_timer > frame_dur:
            self.transition_timer = self.current_time
            frames, idx = frames_and_idx[sizes[self.changing_idx]]
            self.change_player_image(frames, idx)
            self.changing_idx += 1

            if self.changing_idx == len(sizes):
                self.transition_timer = 0
                self.state = 'walk'
                self.right_frames = self.right_big_normal_frames
                self.left_frames = self.left_big_normal_frames

    def big2small(self, keys):
        frame_dur = 65
        sizes = [2, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
        frames_and_idx = [(self.small_normal_frames, 8), (self.big_normal_frames, 8), (self.big_normal_frames, 4)]

        if self.transition_timer == 0:
            self.big = False
            self.transition_timer = self.current_time
            self.changing_idx = 0
        elif self.current_time - self.transition_timer > frame_dur:
            self.transition_timer = self.current_time
            frames, idx = frames_and_idx[sizes[self.changing_idx]]
            self.change_player_image(frames, idx)
            self.changing_idx += 1

            if self.changing_idx == len(sizes):
                self.transition_timer = 0
                self.state = 'walk'
                self.right_frames = self.right_small_normal_frames
                self.left_frames = self.left_small_normal_frames

    def big2fire(self, keys):
        frame_dur = 65
        sizes = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
        frames_and_idx = [(self.big_fire_frames, 3), (self.big_normal_frames, 3)]

        if self.transition_timer == 0:
            self.fire = True
            self.transition_timer = self.current_time
            self.changing_idx = 0
        elif self.current_time - self.transition_timer > frame_dur:
            self.transition_timer = self.current_time
            frames, idx = frames_and_idx[sizes[self.changing_idx]]
            self.change_player_image(frames, idx)
            self.changing_idx += 1

            if self.changing_idx == len(sizes):
                self.transition_timer = 0
                self.state = 'walk'
                self.right_frames = self.right_big_fire_frames
                self.left_frames = self.left_big_fire_frames

    def change_player_image(self, frames, idx):
        self.frame_index = idx
        if self.face_right:
            self.right_frames = frames[0]
            self.image = self.right_frames[self.frame_index]
        else:
            self.left_frames = frames[1]
            self.image = self.left_frames[self.frame_index]
        last_frame_bottom = self.rect.bottom
        last_frame_centerx = self.rect.centerx
        self.rect = self.image.get_rect()
        self.rect.bottom = last_frame_bottom
        self.rect.centerx = last_frame_centerx

    def go_die(self):
        self.dead = True
        self.y_vel = self.jump_vel
        self.frame_index = 6
        self.state = 'die'
        self.death_timer = self.current_time

    '''
      利用速度，加速度，最大速度，方向 实时计算 当前该有的速度
    '''
    def calc_vel(self, vel, accel, max_vel, is_positive=True):
        if is_positive:
            # 不能超过最大速度
            return min(vel + accel, max_vel)
        else:
            return max(vel - accel, -max_vel)

    def calc_frame_duration(self):
        duration = -60 / self.max_run_vel * abs(self.x_vel) + 80
        return duration

    def is_hurt_immune(self):
        if self.hurt_immune:
            if self.hurt_immune_timer == 0:
                self.hurt_immune_timer = self.current_time
                self.blank_image = pygame.Surface((1, 1))
            elif self.current_time - self.hurt_immune_timer < 2000:
                if (self.current_time - self.hurt_immune_timer) % 100 < 50:
                    self.image = self.blank_image
            else:
                self.hurt_immune = False
                self.hurt_immune_timer = 0

    def shoot_fireball(self, level):
        if self.current_time - self.last_fireball_timer > 300:
            self.frame_index = 6
            fireball = powerup.Fireball(self.rect.centerx, self.rect.centery, self.face_right)
            level.powerup_group.add(fireball)
            self.can_shoot = False
            self.last_fireball_timer = self.current_time












